﻿using System;
using System.IO.MemoryMappedFiles;
using System.Runtime.InteropServices;
using System.Threading;

namespace IntoMes.PLC
{
    public class SharedMemory
    {
        #region fields
        private string memoryName;
        private string mutexName;
        private Mutex mutex;
        private MemoryMappedFile memoryObj;
        private MemoryMappedViewAccessor viewAccessor;
        #endregion

        #region methods
        public SharedMemory(string memoryName, string mutexName)
        {
            this.memoryName = memoryName;
            this.mutexName = mutexName;
        }
        /// <summary>
        /// 创建内存映射文件、文件视图、及互斥锁
        /// </summary>
        /// <param name="capacity"></param>
        public void Create(int capacity = 1024)
        {
            memoryObj = MemoryMappedFile.CreateOrOpen(memoryName, capacity);
            viewAccessor = memoryObj.CreateViewAccessor();
            if (!Mutex.TryOpenExisting(mutexName, out mutex))
            {
                mutex = new Mutex(false, mutexName);
            }
        }

        /// <summary>
        /// 将内存中的数据读出转化为对象
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="obj"></param>
        public void Read<T>(ref T obj)
        {
            if (mutex == null || memoryObj == null) return;
            if (mutex.WaitOne())
            {
                //读取对象size的数据然后转换为对象
                int size = Marshal.SizeOf(obj);
                byte[] buffer = new byte[size];
                viewAccessor.ReadArray(0, buffer, 0, size);

                //分配内存
                IntPtr ptr = Marshal.AllocHGlobal(size);
                //拷贝数据
                Marshal.Copy(buffer, 0, ptr, size);
                //转换威对象
                obj = (T)Marshal.PtrToStructure(ptr, typeof(T));
                //释放锁
                mutex.ReleaseMutex();
                //释放内存
                Marshal.FreeHGlobal(ptr);
            }
        }

        /// <summary>
        /// 将对象写入共享内存
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="obj"></param>
        public void Write<T>(ref T obj)
        {
            if (mutex == null || memoryObj == null) return;
            if (mutex.WaitOne())
            {
                int size = Marshal.SizeOf(obj);
                byte[] buffer = new byte[size];

                //分配空间
                IntPtr ptr = Marshal.AllocHGlobal(size);
                //对象转换成位数据
                Marshal.StructureToPtr(obj, ptr, false);
                //拷贝数据
                Marshal.Copy(ptr, buffer, 0, size);
                viewAccessor.WriteArray(0, buffer, 0, size);
                //释放内存
                Marshal.FreeHGlobal(ptr);
                mutex.ReleaseMutex();
            }
        }

        /// <summary>
        /// 释放互斥锁、共享内存映射文件及文件视图
        /// </summary>
        public void Dispose()
        {
            if (mutex != null)
            {
                mutex.Dispose();
            }
            if (viewAccessor != null)
            {
                viewAccessor.Dispose();
            }
            if (memoryObj != null)
            {
                memoryObj.Dispose();
            }
        }
        #endregion
    }
}
